package com.fastwire.carriere.rad.snmpadapter;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.snmp4j.PDU;
import org.snmp4j.Snmp;
import org.snmp4j.Target;
import org.snmp4j.TransportMapping;
import org.snmp4j.event.ResponseEvent;
import org.snmp4j.event.ResponseListener;
import org.snmp4j.smi.Address;
import org.snmp4j.smi.GenericAddress;
import org.snmp4j.smi.Integer32;
import org.snmp4j.smi.Null;
import org.snmp4j.smi.OID;
import org.snmp4j.smi.Variable;
import org.snmp4j.smi.VariableBinding;
import org.snmp4j.transport.DefaultUdpTransportMapping;
import org.snmp4j.util.DefaultPDUFactory;
import org.snmp4j.util.TableEvent;
import org.snmp4j.util.TableUtils;

import com.fastwire.carriere.common.util.CommonLogger;

/*
 * Created on 20120501
 * 
 * @author: Lei Jun
 */
public class SnmpAdmin
{
    private Target target = null;
    private Snmp snmp = null;

    public boolean start(String address, String strCommunity)
    {
        Address targetAddress = GenericAddress.parse(address);
        if (null == targetAddress)
        {
            System.out.println(address + " is not correct.");
            return false;
        }

        try
        {
            @SuppressWarnings("rawtypes")
            TransportMapping transport = new DefaultUdpTransportMapping();
            snmp = new Snmp(transport);

            transport.listen(); // Do not forget this line!

            target = SnmpUtil.createCommunityTarget(targetAddress, strCommunity);
            if(null == target)
            {
                return false;
            }
        }
        catch (IOException e)
        {
            e.printStackTrace();
            return false;
        }

        return true;
    }

    // Since snmp4j relies on asynch req/resp we need a listener
    // for responses which should be closed
    public void stop() throws IOException
    {
        snmp.close();
    }

    public String snmpGet(OID oid) throws IOException
    {
        if (null == target)
        {
            throw new RuntimeException("should set target.");
        }

        ResponseEvent event = null;
        PDU response = null;
        try
        {
            PDU pdu = new PDU();
            pdu.add(new VariableBinding(oid));

            pdu.setType(PDU.GET); // PDU.GETNEXT

            event = snmp.send(pdu, target, null);
            if (null == event)
            {
                return "send ResponseEvent is null.";
            }
            response = event.getResponse();
            if (null == response)
            {
                return "no response received.";
            }
        }
        catch (RuntimeException e)
        {
            CommonLogger.error(this, "GET timed out");
            e.printStackTrace();
        }

        return response.get(0).getVariable().toString();
    }

    public String snmpGetNext(OID oid) throws IOException
    {
        if (null == target)
        {
            throw new RuntimeException("should set target.");
        }

        ResponseEvent event = null;
        PDU response = null;
        try
        {
            PDU pdu = new PDU();
            pdu.add(new VariableBinding(oid));

            pdu.setType(PDU.GETNEXT);

            event = snmp.send(pdu, target, null);
            if (null == event)
            {
                return "send ResponseEvent is null.";
            }
            response = event.getResponse();
            if (null == response)
            {
                return "no response received.";
            }
        }
        catch (RuntimeException e)
        {
            CommonLogger.error(this, "GET timed out");
            e.printStackTrace();
        }

        return response.get(0).getVariable().toString();
    }

    // return the response of the request
    public String snmpSet(OID oid, Variable value) throws IOException
    {
        ResponseEvent event = null;
        try
        {
            PDU pdu = new PDU();

            pdu.add(new VariableBinding(oid, value));

            pdu.setType(PDU.SET);

            event = snmp.set(pdu, target);
        }
        catch (RuntimeException e)
        {
            CommonLogger.error(this, "GET timed out");
            e.printStackTrace();
        }

        // return event.getResponse().get(0).getVariable().toString(); // the old value of the oid
        return event.getResponse().toString();
    }


    // return all the information of the table
    public List<String> snmpWalk(OID oid)
    {
        PDU requestPDU = new PDU();
        requestPDU.setType(PDU.GETNEXT);
        requestPDU.add(new VariableBinding(oid));

        List<String> rspList = new ArrayList<String>();
        try
        {
            boolean finished = false;
            while (!finished)
            {
                VariableBinding vb = null;
                ResponseEvent response = snmp.send(requestPDU, target);
                PDU responsePDU = response.getResponse();

                if (null == responsePDU)
                {
                    System.out.println("responsePDU == null");
                    finished = true;
                    break;
                }
                else
                {
                    vb = responsePDU.get(0);
                }
                // check finish
                finished = checkWalkFinished(oid, responsePDU, vb);
                if (!finished)
                {
                    // Dump response.
                    System.out.println("----" + vb.toString());
                    //System.out.println("----" + vb.getOid().toString());
                    //System.out.println("----" + vb.getVariable().toString());

                    rspList.add(vb.getVariable().toString());
                    
                    // Set up the variable binding for the next entry.
                    requestPDU.setRequestID(new Integer32(0));
                    requestPDU.set(0, vb);
                }
            }
            System.out.println("success finish snmp walk!");
        }
        catch (Exception e)
        {
            System.out.println("SNMP walk Exception: " + e);
        }
        finally
        {
            if (snmp != null)
            {
                try
                {
                    snmp.close();
                }
                catch (IOException ex1)
                {
                    snmp = null;
                }
            }
        }

        return rspList;
    }

    // the same as snmapWalk
    public List<String> getTableAsStrings(OID oid)
    {
        TableUtils tUtils = new TableUtils(snmp, new DefaultPDUFactory());

        List<TableEvent> events = tUtils.getTable(target, new OID[]{oid}, null, null);

        List<String> list = new ArrayList<String>();
        for (TableEvent event : events)
        {
            if (event.isError())
            {
                throw new RuntimeException(event.getErrorMessage());
            }
            for (VariableBinding vb : event.getColumns())
            {
                System.out.println(vb.getOid().toString() + " = " + vb.getVariable().toString());
                list.add(vb.getVariable().toString());
            }
        }

        return list;
    }

    
    // the same as snmapWalk
    public List<List<String>> getTableAsList(OID oid)
    {
        TableUtils tUtils = new TableUtils(snmp, new DefaultPDUFactory());

        List<TableEvent> events = tUtils.getTable(target, new OID[]{oid}, null, null);

        List<List<String>> list = new ArrayList<List<String>>();
        for (TableEvent event : events)
        {
            if (event.isError())
            {
                throw new RuntimeException(event.getErrorMessage());
            }

            List<String> strList = new ArrayList<String>();
            for (VariableBinding vb : event.getColumns())
            {
                //System.out.println(vb.getOid().toString() + " = " + vb.getVariable().toString());
                strList.add(vb.getOid().toString());
                strList.add(vb.getVariable().toString());
            }
            list.add(strList);
        }

        return list;
    }


    /**
     * Normally this would return domain objects or something else than this...
     */
    public List<List<String>> getTableAsStrings(OID[] oids)
    {
        TableUtils tUtils = new TableUtils(snmp, new DefaultPDUFactory());

        List<TableEvent> events = tUtils.getTable(target, oids, null, null);

        List<List<String>> list = new ArrayList<List<String>>();
        for (TableEvent event : events)
        {
            if (event.isError())
            {
                throw new RuntimeException(event.getErrorMessage());
            }
            List<String> strList = new ArrayList<String>();
            list.add(strList);
            for (VariableBinding vb : event.getColumns())
            {
                strList.add(vb.getOid().toString() + " = " + vb.getVariable().toString());
            }
        }

        return list;
    }

    public void getOidAsString(OID oids[], ResponseListener listener)
    {
        try
        {
            PDU pdu = new PDU();
            for (OID oid : oids)
            {
                pdu.add(new VariableBinding(oid));
            }
            pdu.setType(PDU.GETNEXT); // PDU.GET, PDU.GETNEXT;

            snmp.send(pdu, target, null, listener);
        }
        catch (IOException e)
        {
            throw new RuntimeException(e);
        }
    }


    /**
     * check snmp walk finish
     * @param resquestPDU
     * @param targetOID
     * @param responsePDU
     * @param vb
     * @return
     */
    private boolean checkWalkFinished(OID targetOID, PDU responsePDU, VariableBinding vb)
    {
        boolean finished = false;
        if (responsePDU.getErrorStatus() != 0)
        {
            System.out.println("responsePDU.getErrorStatus() != 0 ");
            System.out.println(responsePDU.getErrorStatusText());
            finished = true;
        }
        else if (vb.getOid() == null)
        {
            System.out.println("vb.getOid() == null");
            finished = true;
        }
        else if (vb.getOid().size() < targetOID.size())
        {
            System.out.println("vb.getOid().size() < targetOID.size()");
            finished = true;
        }
        else if (targetOID.leftMostCompare(targetOID.size(), vb.getOid()) != 0)
        {
            System.out.println("targetOID.leftMostCompare() != 0");
            finished = true;
        }
        else if (Null.isExceptionSyntax(vb.getVariable().getSyntax()))
        {
            System.out.println("Null.isExceptionSyntax(vb.getVariable().getSyntax())");
            finished = true;
        }
        else if (vb.getOid().compareTo(targetOID) <= 0)
        {
            System.out.println("Variable received is not "
                    + "lexicographic successor of requested " + "one:");
            System.out.println(vb.toString() + " <= " + targetOID);
            finished = true;
        }

        return finished;
    }
    
    /*
     * public static String extractSingleString(ResponseEvent event) { return
     * event.getResponse().get(0).getVariable().toString(); }
     * 
     * public void onRespnose(ResponseEvent event) { // Always cancel async request when response
     * has been received // otherwise a memory leak is created! Not canceling a request //
     * immediately can be useful when sending a request to a broadcast address. ((Snmp)
     * event.getSource()).cancel(event.getRequest(), (ResponseListener) this);
     * System.out.println("Received response PDU is: " + event.getResponse()); }
     */

    public static void main(String[] args)
    {
        try
        {
            SnmpAdmin snmpAdm = new SnmpAdmin();
            snmpAdm.start("129.9.0.31/161", "private");

            List<String> strList = snmpAdm.getTableAsStrings(new OID("1.3.6.1.4.1.164.12.2.2.1.3")); //snmpWalk(new OID("1.3.6.1.4.1.164.12.2.2.1.3"));  // log
            for(String str : strList)
            {
                System.out.println(str);
            }
            
            String strRslt = snmpAdm.snmpGet(new OID("1.3.6.1.2.1.1.1.0"));

            System.out.println(strRslt);

            /*
             * // sending request ResponseListener listener = new ResponseListener()
             *  { public void
             * onResponse(ResponseEvent event) { // Always cancel async request when response has
             * been // received // otherwise a memory leak is created! Not canceling a // request //
             * immediately can be useful when sending a request to a // broadcast // address.
             * ((Snmp) event.getSource()).cancel(event.getRequest(), this);
             * System.out.println("Received response PDU is: " + event.getResponse()); } };
             * 
             * snmp.send(pdu, target, null, listener);
             */
        }
        catch (IOException e)
        {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}
